/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../macro/head.h"
#include "package_impl.h"

namespace xos_package
{
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static package_impl::ITEM_DATA xos_item_data;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    package_impl::package_impl()
    {
        init_data();
    }

    package_impl::~package_impl()
    {
        term();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // public method

    int package_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int package_impl::init_data()
    {
        int ret = 0;
        m_md5_key = "";
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int package_impl::init()
    {
        int ret = 0;
        return ret;
    }

    int package_impl::term()
    {
        int ret = 0;

        init_data();

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 
    // head md5(2) + body len(2) + body md5(2) + body(n)
    // 
    // 返回值定义：
    //  0  : 成功解到一包数据
    //	1  : 数据不够一包
    // -1  : 是非法的数据包，应该断掉连接
    // -2  : 解包出错，数据包不正确，应该断掉连接
    // 
    int package_impl::un_pack( xos_box::i_list * pOutputList, xos_box::i_list * pInputList )
    {
        int ret = 0;

        xos_box::i_big_buf * pBigBuf = mgr::container()->box()->big_buf();

        while( ( 0 == ret ) && pInputList->front( 0 ) )
        {
            pInputList->get_data( pBigBuf );
            // 
            // 返回值定义：
            //  0  : 成功解到一包数据
            //	1  : 数据不够一包
            // -1  : 是非法的数据包，应该断掉连接
            // -2  : 解包出错，数据包不正确，应该断掉连接
            // 
            while( 0 == ret )
            {
                ret = decode( pBigBuf, pOutputList );
            }
            if( ret >= 0 )
            {
                pBigBuf->move_data_to_begin();
            }
        }

        {
            pInputList->add_data( pBigBuf, true );
        }

        xos_stl::release_interface( pBigBuf );

        return ret;
    }

    // 
    // head md5(2) + body len(2) + body md5(2) + body(n)
    // 
    // 返回值定义
    // 
    // 0  : 成功
    // -1 : 失败，有包数据太长( len <= xos_box::i_buf::BUF_SIZE - 6 )
    // 
    int package_impl::pack( xos_box::i_list * pOutputList, xos_box::i_list * pInputList )
    {
        int ret = 0;

        xos_box::i_buf * pBuf = 0;

        while( ( 0 == ret ) && ( pBuf = ( xos_box::i_buf* )pInputList->front( 0 ) ) )
        {
            ret = encode( pBuf );
            if( 0 == ret )
            {
                pOutputList->push_back( pBuf );
                pInputList->pop_front();
            }
        }

        return ret;
    }

    // 
    // 干扰key,用来混入md5计算，让试图破解的人无从判断包头的计算规则
    // 
    int package_impl::set_md5_key( const char * lpszKey )
    {
        int ret = 0;
        m_md5_key = lpszKey;
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // head md5(2) + body len(2) + body md5(2) + body(n)
    // 
	// 返回值定义：
	//  0  : 成功解到一包数据
	//	1  : 数据不够一包
	// -1  : 是非法的数据包，应该断掉连接
	// -2  : 解包出错，数据包不正确，应该断掉连接
    // 
    int package_impl::decode( xos_box::i_big_buf * pBigBuf, xos_box::i_list * pList )
    {
        int ret = 0;

        const char * lpszKey = m_md5_key.c_str();
        int nKeyLen = (int)m_md5_key.size();

        xos_common::i_md5 * pMd5 = mgr::container()->common()->md5();

        char * lpszData = pBigBuf->get_data( 0, 0, 0, 0 );
        int nBasePos = pBigBuf->get_pos( 0 );

        int nLen = pBigBuf->get_len( 0 );
        int nPos = 0;

        int body_len = 0;

        // 包头长度就是6字节，不可能是个空数据体的包
        if( ( 0 == ret ) && ( nLen < 6 ) )
        {
            ret = 1;
        }
        
        // 包头md5较验
        if( 0 == ret )
        {
            uint16_t uHeadMd5 = 0;
            int nCalMd5 = 0;
            mgr::container()->box()->serial()->un_pack_int16( lpszData, &nPos, &nLen, &uHeadMd5 );
            pMd5->cal_update( lpszData + nPos, 4 );
            pMd5->cal_update( lpszKey, nKeyLen );
            pMd5->get_hash2byte( &nCalMd5 );
            pMd5->clear();
            if( nCalMd5 != uHeadMd5 )
            {
                ret = -2;
            }
        }

        // 包长度是否够，如果不够就不要继续解包了
        if( 0 == ret )
        {
            uint16_t uData = 0;
            mgr::container()->box()->serial()->un_pack_int16( lpszData, &nPos, &nLen, &uData );
            body_len = uData;
            if( nLen < body_len + 2 )
            {
                ret = 1;
            }
        }

        // body的md5校验
        if( 0 == ret )
        {
            uint16_t uBodyMd5 = 0;
            mgr::container()->box()->serial()->un_pack_int16( lpszData, &nPos, &nLen, &uBodyMd5 );
            int nCalMd5 = 0;
            pMd5->cal_update( lpszData + nPos, body_len );
            pMd5->cal_update( lpszKey, nKeyLen );
            pMd5->get_hash2byte( &nCalMd5 );
            pMd5->clear();
            if( nCalMd5 != uBodyMd5 )
            {
                ret = -2;
            }
        }

        // 解出一包数据
        if( 0 == ret )
        {
            xos_box::i_buf * pBuf = mgr::container()->box()->buf();
            pBuf->add_data( lpszData + nPos, body_len, true );
            nPos += body_len;
            nLen -= body_len;
            pList->push_back( pBuf );
            pBuf = 0;
            pBigBuf->set_pos( nBasePos + nPos );
            pBigBuf->set_len( nLen );
        }

        xos_stl::release_interface( pMd5 );

        return ret;
    }

    // 
    // head md5(2) + body len(2) + body md5(2) + body(n)
    // 
    // 返回值定义
    // 
    // 0  : 成功
    // -1 : 失败，有包数据太长( len <= xos_box::i_buf::BUF_SIZE - 6 )
    // 
    int package_impl::encode( xos_box::i_buf * pBuf )
    {
        int ret = 0;

        const char * lpszKey = m_md5_key.c_str();
        int nKeyLen = ( int )m_md5_key.size();

        xos_common::i_md5 * pMd5 = mgr::container()->common()->md5();

        char * lpszData = 0;
        int nBasePos = 0;
        int nLen = 0;
        
        if( 0 == ret )
        {
            lpszData = pBuf->get_data( 0, 0, 0, 0 );
            nBasePos = pBuf->get_pos( 0 );
            nLen = pBuf->get_len( 0 );
        }

        if( ( 0 == ret ) && ( nLen > xos_box::i_buf::BUF_SIZE - 6 ) )
        {
            ret = -1;
        }

        // 数据移到6字节偏移处
        if( 0 == ret )
        {
            mgr::container()->crt()->memmove( lpszData - nBasePos + 6, lpszData, nLen );
            pBuf->set_pos( 0 );
            pBuf->set_len( 0 );
            lpszData = pBuf->get_data( 0, 0, 0, 0 );
        }

        // 计算body的md5,，并放入pBuf对应位置
        if( 0 == ret )
        {
            int nCalMd5 = 0;
            pMd5->cal_update( lpszData + 6, nLen );
            pMd5->cal_update( lpszKey, nKeyLen );
            pMd5->get_hash2byte( &nCalMd5 );
            pMd5->clear();
            pBuf->set_pos( 4 );
            pBuf->set_len( 0 );
            mgr::container()->box()->serial()->pack_int16( pBuf, nCalMd5 );
        }

        // 添入body len
        if( 0 == ret )
        {
            pBuf->set_pos( 2 );
            pBuf->set_len( 0 );
            mgr::container()->box()->serial()->pack_int16( pBuf, nLen );
        }

        // 计算head的md5,，并放入pBuf对应位置
        if( 0 == ret )
        {
            int nCalMd5 = 0;
            pMd5->cal_update( lpszData + 2, 4 );
            pMd5->cal_update( lpszKey, nKeyLen );
            pMd5->get_hash2byte( &nCalMd5 );
            pMd5->clear();
            pBuf->set_pos( 0 );
            pBuf->set_len( 0 );
            mgr::container()->box()->serial()->pack_int16( pBuf, nCalMd5 );
        }

        // 成功了，正确添写pBuf的pos和len
        if( 0 == ret )
        {
            pBuf->set_pos( 0 );
            pBuf->set_len( nLen + 6 );
        }

        xos_stl::release_interface( pMd5 );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

} // xos_package
